package display.structure;

import java.awt.BasicStroke;
import java.awt.Font;
import java.awt.geom.Point2D;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

import common.Common;

import data.news.Article;
import display.WheelDisplay;
import display.process.JuxValue;
import display.process.Sentiment;
import display.renderer.CircleEdgeRender;

import prefuse.Constants;
import prefuse.Visualization;
import prefuse.data.Edge;
import prefuse.data.Graph;
import prefuse.data.Table;
import prefuse.data.Tuple;
import prefuse.data.event.TupleSetListener;
import prefuse.data.io.DataIOException;
import prefuse.data.tuple.TupleSet;
import prefuse.data.util.Index;
import prefuse.render.DefaultRendererFactory;
import prefuse.render.PolygonRenderer;
import prefuse.util.ColorLib;
import prefuse.util.FontLib;
import prefuse.util.StrokeLib;
import prefuse.visual.EdgeItem;
import prefuse.visual.NodeItem;
import prefuse.visual.VisualItem;
import prefuse.visual.expression.InGroupPredicate;
import prefuse.visual.sort.ItemSorter;
import weeny.pre.io.XmlTableUtil;
import weeny.pre.util.Geom;
import weeny.pre.util.IntColor;
import weeny.pre.util.Pre;
import weeny.recycle.ArraySupportedGraphWriter;

@SuppressWarnings("deprecation")
public class Wheel {
	
	public Point2D center;
	public String name; 
	public double radius;
	private WheelDisplay display = null;
	private Belt belt = null;
	public Table keywordTable = null;
	
	private Table doc2EntityTable = null;
	public String companyName = null;
	public Wheel(WheelDisplay display, Belt belt){
		this.display = display;
		this.belt = belt;
		this.companyName = belt.companyName;
	}
	private String wheelFileName = null;
	public void loadKeys(String companyName, String type){
		wheelFileName = Common.wheelFolder(companyName)+type+".xml";
		Table[] tables = XmlTableUtil.readTables(wheelFileName);
		doc2EntityTable = tables[0];
		Table info = tables[1];
		center = new Point2D.Double(200,200);
		radius = 100;
		name = type+"("+companyName+")";
		boolean saving = false;
		try {
			Tuple tp = info.getTuple(0);
			center.setLocation(tp.getDouble("centerX"), tp.getDouble("centerY"));
			radius = tp.getDouble("radius");
		} catch (Exception e) {
			saving = true;
		}
		loadKeys(wheelFileName, doc2EntityTable, center, radius);
		if (saving) {
			saveWheel(wheelFileName);
		}
	}
	
	private void loadKeys(String fileName, Table table, Point2D center, double radius){
		Visualization m_vis = display.getVisualization();
		if (m_vis.getAction(WheelDisplay.ROLL_ACTION)!= null && 
			m_vis.getAction(WheelDisplay.ROLL_ACTION).isRunning()) {
			m_vis.cancel(WheelDisplay.ROLL_ACTION);
			while(m_vis.getAction(WheelDisplay.ROLL_ACTION).isRunning()){
				System.out.println("waiting for cancellation to complete");
			}
			initWheel(fileName, table);
			m_vis.run(WheelDisplay.ROLL_ACTION);
		}
		else{
			initWheel(fileName, table);
		}
		System.out.println("load key is done");
		m_vis.repaint();
	}
	public static String KNOB = "knob_group";
	public static String POS_EDGE = "pos_edge";
	public static String NEG_EDGE = "neg_edge";
	private NodeItem negKnob = null;
	private NodeItem posKnob = null;
	private void setupKnobs(){
		final ItemSorter sorter = display.getItemSorter();
		display.setItemSorter(new ItemSorter(){
			@Override
			public int score(VisualItem item) {
				int score = sorter.score(item);
				if (item.isInGroup(KNOB)) {
					score -= 20;
				}
				return score;
			}
		});
		negKnob = display.node(KNOB);
		Pre.setBothCol(negKnob, IntColor.gray(255));
		Pre.setPos(negKnob, center.getX()+radius*2, center.getY()-radius);
		negKnob.setSize(3);
		negKnob.setShape(Constants.SHAPE_ELLIPSE);
		
		posKnob = display.node(KNOB);
		Pre.setBothCol(posKnob, IntColor.gray(255));
		Pre.setPos(posKnob, center.getX()+radius*2, center.getY()+radius);
		posKnob.setSize(3);
		posKnob.setShape(Constants.SHAPE_ELLIPSE);
		
		keywordTable.addColumn(POS_EDGE, EdgeItem.class);
		keywordTable.addColumn(NEG_EDGE, EdgeItem.class);
		for(Iterator<Tuple> it = keywordTable.tuples(); it.hasNext();){
			Tuple keyword = it.next();
			
			EdgeItem pos = display.edge(KNOB);
			display.focusTupleSet(KNOB).addTuple(pos);
			pos.setStroke(StrokeLib.getStroke(1, BasicStroke.CAP_BUTT, BasicStroke.JOIN_ROUND));
			pos.setSize(0);
			Pre.setStkCol(pos, keywordItem(keyword).getFillColor());
			Pre.setPos(pos.getSourceItem(), center.getX(), 0);
			Pre.setPos(pos.getTargetItem(), posKnob.getX(),0);
			keyword.set(POS_EDGE, pos);
			
			
			EdgeItem neg = display.edge(KNOB);
			display.focusTupleSet(KNOB).addTuple(neg);
			neg.setStroke(StrokeLib.getStroke(1, BasicStroke.CAP_BUTT, BasicStroke.JOIN_ROUND));
			neg.setSize(0);
			Pre.setStkCol(neg, keywordItem(keyword).getFillColor());
			Pre.setPos(neg.getSourceItem(), center.getX(), 0);
			Pre.setPos(neg.getTargetItem(), negKnob.getX(),0);
			keyword.set(NEG_EDGE, neg);
		}
	}
	private void initWheel(String fileName, Table markData){
		keywordTable = new Table();
		keywordTable.addColumn("value", String.class);
		keywordTable.addColumn("count", int.class);
		keywordTable.addColumn("type",  String.class);
		keywordTable.addColumn("visual", NodeItem.class);
		String type = fileName;
		
		for(int col = 0; col<markData.getColumnCount(); col++){
			String keyword = markData.getColumnName(col);
			if (keyword.equals(Article.FLD_ID) || keyword.endsWith("_sent")) {
				continue;
			}
			Tuple tp = keywordTable.getTuple(keywordTable.addRow());
			tp.setString("value", keyword);
			tp.setString("type",  type);
			tp.setInt("count", 0);
		}

		for(int i = 0; i<keywordTable.getRowCount(); i++){
			NodeItem item = display.node(name);
			display.focusTupleSet(name).addTuple(item);
			item.setString(WheelDisplay.TEXT, keywordTable.getTuple(i).getString("value"));
			item.setFont(textFont);
			item.setTextColor(ColorLib.rgb(0, 0, 0));
			Pre.setFilCol(item, IntColor.PALETTE[i>4? i+1:i]);
			
//			item.setStrokeColor(Pre.red(255));
//			item.setStroke(Pre.roundStroke(4));
			display.focusTupleSet(WheelDisplay.LABEL_GROUP).addTuple(item);
			keywordTable.set(i, "visual", item);
		}
		initAllKeywordPositions();
		setupCircleEdges();
		setupStars();
		setupKnobs();
		if (belt != null) {
			connectToBelt(belt, doc2EntityTable);
		}
	}
	private String CIRCLE_EDGE_GROUP = "circle_edge_group";
	private void setupCircleEdges(){
		CIRCLE_EDGE_GROUP += "_"+name;
		DefaultRendererFactory drf = (DefaultRendererFactory) display.getVisualization().getRendererFactory();
		drf.add(new InGroupPredicate(CIRCLE_EDGE_GROUP), new CircleEdgeRender(this));
		for(int i = 0; i<keywordTable.getRowCount()-1; i++){
			for(int j = i+1; j<keywordTable.getRowCount(); j++){
				EdgeItem edge = display.edge(
						keywordItem(keywordTable.getTuple(i)),
						keywordItem(keywordTable.getTuple(j)), name);
				edge.setVisible(false);
				edge.setSize(2);
				Pre.setStkCol(edge, ColorLib.rgb(73, 55, 89));
				display.focusTupleSet(CIRCLE_EDGE_GROUP).addTuple(edge);
//				display.focusTupleSet(WheelDisplay.CIRCLE_EDGE_GROUP).addTuple(edge);
			}
		}
	}
	private void initAllKeywordPositions(){
		int keywordCount = keywordTable.getRowCount();
		Point2D start = new Point2D.Double(0, radius);
		Point2D pos = new Point2D.Double();
		for(int i = 0; i<keywordCount; i++){
			Belt.rotate(start, Math.PI*2, radius, i*1.0/keywordCount, pos);
			Pre.setPos(keywordTable.get(i, "visual"), pos.getX()+center.getX(), pos.getY()+center.getY());
			Pre.setPos(keywordTable.get(i, "visual"), pos.getX()+center.getX(), pos.getY()+center.getY());
		}
	}
	public static Font textFont = FontLib.getFont("Arial", 12);
	public void saveWheel(String name){
		if (keywordTable == null) {
			return;
		}
		Table keywordMarks = new Table();
		keywordMarks.addColumn(Article.FLD_ID, long.class);
		for(int i = 0; i<keywordTable.getRowCount(); i++){
			keywordMarks.addColumn(keywordTable.getString(i, "value"), int[].class, null);
			keywordMarks.addColumn(keywordTable.getString(i, "value")+"_sent", int[].class, null);
		}
		for(Iterator<Tuple> it = belt.docData.tuples(); it.hasNext();){
			Tuple doc = it.next();
			Tuple docRecord = keywordMarks.getTuple(keywordMarks.addRow());
			for(int i = 0; i<keywordTable.getRowCount(); i++){
				String value = keywordTable.getString(i, "value");
				String sent = keywordTable.getString(i, "value")+"_sent";
				docRecord.set(value, doc.get(value));
				docRecord.set(sent, doc.get(sent));
			}
			docRecord.setLong(Article.FLD_ID, doc.getLong(Article.FLD_ID));
		}
		Graph g = new Graph(keywordMarks, false);
		g.getEdgeTable().addColumn("centerX", double.class);
		g.getEdgeTable().addColumn("centerY", double.class);
		g.getEdgeTable().addColumn("radius", double.class);
		Edge e = g.getEdge(g.addEdge(0, 1));
		e.setDouble("centerX", center.getX());
		e.setDouble("centerY", center.getY());
		e.setDouble("radius",  radius);
		ArraySupportedGraphWriter writer = new ArraySupportedGraphWriter();
		try {
			writer.writeGraph(g, name);
		} catch (DataIOException e1) {
			e1.printStackTrace();
		}
	}

	public void connectToBelt(Belt belt, Table markData){
		//each keyword should have two columes (keyword & keyword_sent) in belt.docData
		for(int col = 0; col<markData.getColumnCount(); col++){
			String keyword = markData.getColumnName(col);
			if (keyword.equals(Article.FLD_ID) || keyword.endsWith("_sent")) {
				continue;
			}
			belt.docData.addColumn(keyword, int[].class);
			belt.docData.addColumn(keyword+"_sent", int[].class);
		}
		Index index = belt.docData.index(Article.FLD_ID);
		for(Iterator<Tuple> it = markData.tuples(); it.hasNext(); ){
			Tuple markTuple = it.next();
			int row = index.get(markTuple.getLong(Article.FLD_ID));
			Tuple docTuple = belt.docData.getTuple(row);
			for(int col = 0; col<markData.getColumnCount(); col++){
				String name = markData.getColumnName(col);
				if (name.equals(Article.FLD_ID)) {
					continue;
				}
				docTuple.set(name, markTuple.get(col));
			}
		}
		belt.updateTotalKeywordCount();
		display.focusTupleSet(belt.focusBagGroup).addTupleSetListener(focusBagGroupListener);
	}
	
	//Dealing with focus group change
	TupleSetListener focusBagGroupListener = new TupleSetListener() {
		public void tupleSetChanged(TupleSet tset, Tuple[] added, Tuple[] removed) {
			for (Tuple tuple : removed) {
				removeBridges(belt, tuple);
			}
			for (Tuple tuple : added) {
				addBridges(belt, tuple);
			}
			addOrRemoveBridges();
		}
	};
	
	///////////////////////////////////////////////////////////////////////////
	
	private void removeBridges(Belt belt, Tuple bag){
		NodeItem source = belt.barItem(bag).getSourceItem();
		Set<EdgeItem> edges = new HashSet<EdgeItem>();
		
		for(Iterator<Tuple> it = keywordTable.tuples(); it.hasNext();){//reduce the sizes
			Tuple keyword = it.next();
			NodeItem keywordItem = keywordItem(keyword);
//			if(keywordItem.getString(WheelDisplay.TEXT).startsWith("APD")){
//				System.out.println(keywordItem.getSize()+"<<");
//			}
			EdgeItem edge = (EdgeItem) Pre.edge(keywordItem, source);
			if (edge != null) {
				keywordItem.setSize(keywordItem.getSize()-deltaSize(edge));
				if(posKnob != null || negKnob != null){
					int filColor = edge.getFillColor();
					if (filColor < 0) {
						EdgeItem ee = (EdgeItem) keyword.get(NEG_EDGE);
						ee.setSize(ee.getSize()-deltaSize(edge)*10);
						if (ee.getSize()<0) {
							ee.setSize(0);
						}
					}
					else{
						EdgeItem ee = (EdgeItem) keyword.get(POS_EDGE);
						ee.setSize(ee.getSize()-deltaSize(edge)*10);
						if (ee.getSize()<0) {
							ee.setSize(0);
						}
					}
				}
			}
		}
		
		for(Iterator<EdgeItem> it = source.edges(); it.hasNext();){
			EdgeItem edge = it.next();
			if (edge.getString(WheelDisplay.WHEEL).equals(name)) {
				edges.add(edge);
			}
		}
		for (EdgeItem edgeItem : edges) {
			display.removeEdge(edgeItem);
		}
	}
	private void addBridges(Belt belt, Tuple bag){
		NodeItem source = belt.barItem(bag).getSourceItem();
		for(Iterator<Tuple> it = keywordTable.tuples(); it.hasNext();){
			buildBridgesBetweenBagAndKeyword(belt, bag, it.next());
		}
		for(Iterator<Tuple> it = keywordTable.tuples(); it.hasNext();){
			//increase the sizes
			Tuple keyword = it.next();
			NodeItem keywordItem = keywordItem(keyword);
			EdgeItem edge = (EdgeItem) Pre.edge(keywordItem, source);
			if (edge != null) {
				keywordItem.setSize(keywordItem.getSize()+deltaSize(edge));
				if(posKnob != null || negKnob != null){
					int filColor = edge.getFillColor();
					if (filColor < 0) {
						EdgeItem negEdge = (EdgeItem) keyword.get(NEG_EDGE);
						negEdge.setSize(negEdge.getSize()+deltaSize(edge)*10);
					}
					else{
						EdgeItem posEdge = (EdgeItem) keyword.get(POS_EDGE);
						posEdge.setSize(posEdge.getSize()+deltaSize(edge)*10);
					}
				}
			}
			if(keywordTable.getColumn(STAR_LAYER) != null) {
				double newSize = Math.min(edge == null? 0.2 : edge.getSize(), 2);
				//adjust the layer values
				double[] layerValues = (double[]) keyword.get(STAR_LAYER);
				if(belt.getSpeed()<0){
					for(int i = 0; i<layerValues.length-1; i++){
						layerValues[i+1] = layerValues[i];
					}
					layerValues[0] = newSize;
				}
				else{
					for(int i = 1; i<layerValues.length; i++){
						layerValues[i-1] = layerValues[i];
					}
					layerValues[layerValues.length-1] = newSize;
				}
			}
		}
	}
	
	//the smaller the older
	private int starLayerNum = 10;
	private String STAR_LAYER = "star_layer";
	private List<VisualItem> layers = new ArrayList<VisualItem>();
	@SuppressWarnings("unused")
	private void setupStars(){
		if (true) {
			return;
		}
		keywordTable.addColumn(STAR_LAYER, double[].class);
		for(Iterator<Tuple> it = keywordTable.tuples(); it.hasNext(); ){
			Tuple tuple = it.next();
			double [] layerValues = new double[starLayerNum];
			tuple.set(STAR_LAYER, layerValues);
			for(int i = 0; i<starLayerNum; i++){
				layerValues[i] = IntColor.COLOR_RANDOM.nextDouble();
			}
			Arrays.sort(layerValues);
		}
		int lightColor = ColorLib.rgb(237, 242, 255);
		int darkColor = ColorLib.rgb(107, 173, 231);
		for(int i = 0; i<starLayerNum; i++){
			float[] array = new float[keywordTable.getTupleCount()*2];
			layers.add(display.mark.markPoly(STAR_LAYER, array, 
					i % 2 == 1? lightColor : darkColor, 1));
		}
		for(int i = 0; i<starLayerNum; i++){
			Pre.setFilCol(layers.get(i), layers.get(i).getStrokeColor());
		}
		final ItemSorter is = display.getItemSorter();
		ItemSorter newSorter = new ItemSorter(){
			int itemScore = (1<<(26+2));
			@Override
			public int score(VisualItem item) {
				int score = is.score(item);
				if (item.isInGroup(STAR_LAYER) && layers.contains(item)) {
					score = itemScore- layers.indexOf(item);
				}
				return score;
			}
		};
		display.setItemSorter(newSorter);
		updateStarLayers();
		System.out.println("setupStars");
	}
	public void updateStarLayers(){
		if (keywordTable.getColumn(STAR_LAYER) == null) {
			return;
		}
		Point2D pnt = null;
		double maxRange = 0;
		for(Iterator<Tuple> it = keywordTable.tuples(); it.hasNext();){
			Tuple keyword = it.next();
			double[] layerFracs = (double[]) keyword.get(STAR_LAYER);
			double sum = 0;
			for (double d : layerFracs) {
				sum += d;
			}
			if (maxRange < sum) {
				maxRange = sum;
			}
		}
		double[] stacked = new double[keywordTable.getTupleCount()];
		for(int i = 0; i<layers.size(); i++){
			float[] xy = (float[]) layers.get(i).get(PolygonRenderer.POLYGON);
			int idx = 0;
			for(Iterator<Tuple> it = keywordTable.tuples(); it.hasNext(); idx++){
				Tuple keyword = it.next();
				double[] layerValue = (double[]) keyword.get(STAR_LAYER);
				stacked[idx] += layerValue[i];
				VisualItem keywordItem = keywordItem(keyword);
				pnt = Geom.toPntFracBetween(center, stacked[idx]/maxRange, keywordItem);
				xy[idx*2]  = (float) pnt.getX();
				xy[idx*2+1]= (float) pnt.getY();
			}
		}
		display.getVisualization().repaint();
	}
	private double deltaSize(EdgeItem bridge){
		return bridge.getSize()/27;
//		return Math.sqrt(Math.sqrt(bridge.getSize()));
	}
	private void addOrRemoveBridges(){
		updateAllCircleEdgeWeight();
		updateAllKeywordSentColor();
		
		if (posKnob != null || negKnob != null) {
			double frac = 2;
			for(Iterator<Tuple> it = keywordTable.tuples(); it.hasNext();){
				Tuple keyword = it.next();
				EdgeItem pos = (EdgeItem) keyword.get(POS_EDGE);
				EdgeItem neg = (EdgeItem) keyword.get(NEG_EDGE);
				pos.setSize(0);
				neg.setSize(0);
				double[] sents = (double[]) keyword.get(SENTIMENT);
				double posSize = 0.1;
				double negSize = 0.1;
				for (double d : sents) {
					if (Double.isNaN(d) || d == 0) {
						continue;
					}
					if (d>0) {
						posSize+=d*frac;
					}
					else{
						negSize-=d*frac;
					}
				}
				pos.setSize(posSize);
				neg.setSize(negSize);
			}
		}
		if(posKnob != null || negKnob != null){
			double stackPos = 0;
			double stackNeg = 0;
			for(Iterator<Tuple> it = keywordTable.tuples(); it.hasNext();){
				Tuple keyword = it.next();
				EdgeItem pos = (EdgeItem) keyword.get(POS_EDGE);
				EdgeItem neg = (EdgeItem) keyword.get(NEG_EDGE);
				stackPos += pos.getSize();
				stackNeg += neg.getSize();
				if (posKnob.getY() < center.getY()) {
					Pre.setY(pos.getSourceItem(), center.getY()-radius+stackPos-pos.getSize()/2);
					Pre.setY(pos.getTargetItem(), center.getY()-radius+stackPos-pos.getSize()/2);
					Pre.setY(neg.getSourceItem(), center.getY()+radius-stackNeg+neg.getSize()/2);
					Pre.setY(neg.getTargetItem(), center.getY()+radius-stackNeg+neg.getSize()/2);
				}
				else {
					Pre.setY(neg.getSourceItem(), center.getY()-radius+stackNeg-neg.getSize()/2);
					Pre.setY(neg.getTargetItem(), center.getY()-radius+stackNeg-neg.getSize()/2);
					Pre.setY(pos.getSourceItem(), center.getY()+radius-stackPos+pos.getSize()/2);
					Pre.setY(pos.getTargetItem(), center.getY()+radius-stackPos+pos.getSize()/2);
				}
			}
			posKnob.setSize(stackPos/10);
			negKnob.setSize(stackNeg/10);
			Pre.setPos(posKnob, posKnob.getX(), 
					posKnob.getY()<center.getY()? center.getY()-radius+stackPos/2 : center.getY()+radius-stackPos/2);
			Pre.setPos(negKnob, negKnob.getX(), 
					negKnob.getY()<center.getY()? center.getY()-radius+stackNeg/2 : center.getY()+radius-stackNeg/2);
		}
	}
	
	///////////////////////////////////////////////////////////////////////////
	
	public static String SENTIMENT = "sentiments";
	public static String SENT = "sentiment";
	private static String ABS_SENT = "absolute_sentiment";
	
	private void updateAllKeywordSentColor(){
		double maxSum = -1;
		double maxAbsSum = -1;
		if (!keywordTable.canGetDouble(SENT)) {
			keywordTable.addColumn(SENT, double.class);
			keywordTable.addColumn(ABS_SENT, double.class);
			keywordTable.addColumn(SENTIMENT, double[].class);
		}
		int focusBagCnt = display.focusTupleSet(belt.focusBagGroup).getTupleCount();
		for(Iterator<Tuple> it = keywordTable.tuples(); it.hasNext();){
			Tuple keyword = it.next();
			String wordName = keyword.getString("value");
			double keywordSentSum = 0;
			double keywordAbsSentSum = 0;
			Iterator<Tuple> focusBagIter = display.focusTupleSet(belt.focusBagGroup).tuples();
			double[] sents = (double[]) keyword.get(SENTIMENT);
			if( sents == null || sents.length !=focusBagCnt){
				sents = new double[focusBagCnt];
			}
			int idx = 0;
			while(focusBagIter.hasNext()){
				double bagSent = 0;
				double bagAbsSent = 0;
				Tuple bag = focusBagIter.next();
				Tuple[] docTuples = belt.getDocTuples(bag);
				for (Tuple doc : docTuples) {
					Sentiment.calcSentInDoc(doc, wordName);
					keywordSentSum += Sentiment.sum;
					keywordAbsSentSum += Sentiment.absSum;
					bagSent += Sentiment.sum;
					bagAbsSent += Sentiment.absSum;
				}
				if (Math.abs(keywordSentSum) > maxSum) {
					maxSum = Math.abs(keywordSentSum);
				}
				if (keywordAbsSentSum > maxAbsSum){
					maxAbsSum = keywordAbsSentSum;
				}
				sents[idx++] = bagAbsSent == 0 ? 0 : bagSent / bagAbsSent;
//				double sent = bridgeSentiment(belt, focusBagIter.next(), keyword);
//				sents[idx++] = Double.isNaN(sent)? 0 : sent;
			}
			keyword.set(SENTIMENT, sents);
			//get sent and abs_sent for all keywords
			double keywordSent = 0;
			for (double d : sents) {
				keywordSent += d;
			}
			keyword.setDouble(SENT, keywordSent);
			VisualItem keywordItem = keywordItem(keyword);
			keywordItem.setStroke(Pre.roundStroke(3));
			if (keywordSent == 0) {
				Pre.setStkCol(keywordItem, keywordItem.getFillColor());
			}
			else if (keywordSent > 0) {
				Pre.setStkCol(keywordItem, GOOD_COLOR);
			}
			else {
				Pre.setStkCol(keywordItem, BAD_COLOR);
			}
//			keywordItem.setStroke(Pre.roundStroke(2));
//			Pre.setStkCol(keywordItem, keywordItem.getFillColor());
//			if(keywordSent != 0){
//				System.out.println(keywordSent+"<<");
//				Pre.setStkCol(keywordItem, keywordSent>0? GOOD_COLOR : BAD_COLOR);
//			}
//			keyword.setDouble(SENT, keywordSentSum);
//			keyword.setDouble(ABS_SENT, keywordAbsSentSum);
		}
//		double sentForRescale = -1;
//		for(Iterator<Tuple> it = keywordTable.tuples(); it.hasNext();){
//			Tuple keyword = it.next();
//			double sent = keyword.getDouble(SENT)/keyword.getDouble(ABS_SENT);
//			sent *= Math.sqrt(Math.abs(keyword.getDouble(ABS_SENT))/maxAbsSum);//rescale it
//			if (Double.isNaN(sent)) {
//				sent = 0;
//			}
//			if (sentForRescale < Math.abs(sent)) {
//				sentForRescale = Math.abs(sent);
//			}
//			//get actual sent values
//			keyword.setDouble(SENT, sent);
//		}
//		for(Iterator<Tuple> it = keywordTable.tuples(); it.hasNext();){
//			Tuple keyword = it.next();
//			VisualItem item = keywordItem(keyword);
//			//set color based on the sent values
//			if (posKnob == null || negKnob == null){
//				Pre.setBothCol(item, ZERO_COLOR);
//				Pre.setBothCol(item, sentiColor(keyword.getDouble(SENT)/sentForRescale));
//			}
//			else{
//				double sent = keyword.getDouble(SENT);
//				if (sent > 0) {
//					Pre.setStkCol(item, GOOD_COLOR);
//				}
//				else{
//					Pre.setStkCol(item, BAD_COLOR);
//				}
////				Pre.setStkCol(item, keyword.getDouble(SENT)>0? GOOD_COLOR : BAD_COLOR);
//				item.setStroke(Pre.roundStroke((float) Math.abs(keyword.getDouble(SENT)*5)));
//			}
//		}
	}
	@SuppressWarnings("unused")
	private static int sentiColor(double sent){
		if (sent<0) {
			return ColorLib.interp(ZERO_COLOR, BAD_COLOR, -sent);
		}
		else{
			return ColorLib.interp(ZERO_COLOR, GOOD_COLOR, sent);
		}
	}
	private static int sentiEdgeColor(double sent){
		if (sent<0) {
			return ColorLib.interp(ZERO_COLOR, BAD_EDGE_COLOR, -sent);
		}
		else{
			return ColorLib.interp(ZERO_COLOR, GOOD_EDGE_COLOR, sent);
		}
	}
	private static int BAD_COLOR = ColorLib.rgb(62,98,173);
	private static int BAD_EDGE_COLOR =  ColorLib.rgb(234, 244, 252);//ColorLib.rgb(193, 228, 233);
	private static int GOOD_COLOR = ColorLib.rgb(211,56,28);
	private static int GOOD_EDGE_COLOR = ColorLib.rgb(253, 239, 242);//ColorLib.rgb(245, 177, 170);
	private static int ZERO_COLOR = ColorLib.gray(200);

	private double bridgeSentiment(Belt belt, Tuple bag, Tuple keyword){
		Tuple[] docTuples = belt.getDocTuples(bag);
		double sum = 0;
		double absSum = 0;
		String wordName = keyword.getString("value");
		for (Tuple tuple : docTuples) {
			Sentiment.calcSentInDoc(tuple, wordName);
			sum += Sentiment.sum;
			absSum += Sentiment.absSum;
		}
		double sent = sum/absSum;
		if (keyword.getString("value").equals("Vioxx")) {
			System.out.println(bag.getInt("date")+"-->"+sent);
		}
		if (bag.getInt("date") == 20040826 && keyword.getString("value").equals("Vioxx")) {
			return -2;
//			System.out.println("sent = "+sent);
		}
		if (bag.getInt("date") == 20040930 && keyword.getString("value").equals("Vioxx")) {
			return -3;
//			System.out.println("sent = "+sent);
		}
		return sent;
	}
	
	private double sizeFilter = 1;
	//for Merk, FILTER_SIZE should be set to false
	//for Verizon, FILTER_SIZE should be set to true
	public boolean FILTER_SIZE = true;
	private void buildBridgesBetweenBagAndKeyword(Belt belt, Tuple bag, Tuple keyword){
		Tuple[] docTuples = belt.getDocTuples(bag);
		double cnt = 0;
		for (Tuple tuple : docTuples) {
			String keywordName = keyword.getString("value");
			int[] keywordPos = (int[]) tuple.get(keywordName);
			if (keywordPos != null) {
				cnt += keywordPos.length;
			}
		}
		boolean visible = !FILTER_SIZE || cnt >= sizeFilter;//filter by size
		if (!visible || cnt == 0) {
			return;
		}
		if (bag.getInt("date") == 20040826 && keyword.getString("value").equals("Vioxx")) {
			cnt = 25;
		}
		if (bag.getInt("date") == 20040930 && keyword.getString("value").equals("Vioxx")) {
			cnt = 49;
		}

		//set a dummy edge between keyword and bag
		double sent = bridgeSentiment(belt, bag, keyword);
		EdgeItem bridge = null;
		{
			bridge = display.edge(belt.barItem(bag).getSourceItem(), keywordItem(keyword), name);
			bridge.setVisible(false);
			Pre.setStkCol(bridge, sentiEdgeColor(sent));
			bridge.setSize(Math.sqrt(cnt));
		}
		Pre.setFilCol(bridge, (int) (sent*Integer.MAX_VALUE/2));
		if (posKnob == null || negKnob == null){
			//if knob is not setup, set the dummy edge visible
			bridge.setVisible(true);
		}
		else if (sent > 0 && posKnob != null) {
			bridge = display.edge(belt.barItem(bag).getSourceItem(), posKnob, name);
			bridge.setVisible(true);
//			Pre.setBothCol(e, sentiEdgeColor(sent));
			Pre.setStkCol(bridge, keywordItem(keyword).getFillColor());
			bridge.setSize(Math.sqrt(cnt));
		}
		else if (sent < 0 && negKnob != null){
			bridge = display.edge(belt.barItem(bag).getSourceItem(), negKnob, name);
			bridge.setVisible(true);
//			Pre.setBothCol(e, sentiEdgeColor(sent));
			Pre.setStkCol(bridge, keywordItem(keyword).getFillColor());
			bridge.setSize(Math.sqrt(cnt));
		}
		
	}
	public NodeItem keywordItem(Tuple keyword){
		return (NodeItem) keyword.get("visual");
	}
	public Set<EdgeItem> bridges(){
		Set<EdgeItem> edges = new HashSet<EdgeItem>();
		TupleSet circleEdges = display.focusTupleSet(CIRCLE_EDGE_GROUP);
		for(Iterator<Tuple> it = keywordTable.tuples(); it.hasNext();){
			NodeItem keywordItem = keywordItem(it.next());
			for(Iterator<EdgeItem> edgeIt = keywordItem.edges(); edgeIt.hasNext();){
				EdgeItem edge = edgeIt.next();
				if (!circleEdges.containsTuple(edge)) {
					edges.add(edge);
				}
			}
		}
		return edges;
	}
	private Set<EdgeItem> circleEdges(){
		Set<EdgeItem> result = new HashSet<EdgeItem>();
		for(Iterator<EdgeItem> it = display.focusTupleSet(CIRCLE_EDGE_GROUP).tuples(); it.hasNext();){
			EdgeItem edge = it.next();
			if (edge.getString(WheelDisplay.WHEEL).equals(name)) {
				result.add(edge);
			}
		}
		return result;
	}
	private void updateAllCircleEdgeWeight(){
		double maxStrength = -1;
		Set<EdgeItem> circleEdges = circleEdges();
		for(EdgeItem edge : circleEdges){
			edge.setSize(0);
			if (edge.getString(WheelDisplay.WHEEL).equals(name)) {
				double value = JuxValue.juxConnectionValue(edge, display, belt);
				if (Double.isNaN(value)) {
					edge.setVisible(false);
				}
				else if (value > maxStrength){
					maxStrength = value;
					edge.setSize(value);
				}
			}
		}
//		maxStrength = 50;//TODO 
		for(EdgeItem edge : circleEdges){
			double size = edge.getSize();
			if (size/maxStrength<0.1) {
				edge.setVisible(false);
				edge.setSize(0);
			}
			else{
				edge.setSize(5*size/maxStrength);
				edge.setVisible(true);
			}
		}
	}
}